use crate::badge::color::parser::parse_color;
use crate::badge::color::Color;
use crate::badge::{DEFAULT_LABEL_COLOR, DEFAULT_MESSAGE_COLOR};
use crate::error::Error;

pub(crate) fn get_color_from_option(
    is_label: bool,
    color_string: Option<String>,
    color_enum: Option<&Color>,
) -> Result<String, Error> {
    if let Some(color) = color_enum {
        Ok(match color {
            Color::Named(n) => n.hex().to_string(),
            Color::Alias(a) => a.hex().to_string(),
            &Color::Rgb(r, g, b) => {
                format!("#{}", rgb_to_hex(r, g, b))
            }
        })
    } else if let Some(color) = color_string {
        parse_color(&color)
    } else {
        Ok(if is_label {
            DEFAULT_LABEL_COLOR.hex().to_string()
        } else {
            DEFAULT_MESSAGE_COLOR.hex().to_string()
        })
    }
}

pub(crate) fn brightness(color: &str) -> f32 {
    let (r, g, b) = hex_color_to_rgb(&color[1..]);
    (r * 299 + g * 587 + b * 114) as f32 / 255000f32
}

fn hex_to_u32_one(c: &char) -> u32 {
    match c {
        '0' => 0,
        '1' => 1,
        '2' => 2,
        '3' => 3,
        '4' => 4,
        '5' => 5,
        '6' => 6,
        '7' => 7,
        '8' => 8,
        '9' => 9,
        'a' => 10,
        'b' => 11,
        'c' => 12,
        'd' => 13,
        'e' => 14,
        'f' => 15,
        _ => panic!("unknown digit"),
    }
}

fn u4_to_hexit(u: u8) -> char {
    match u {
        0 => '0',
        1 => '1',
        2 => '2',
        3 => '3',
        4 => '4',
        5 => '5',
        6 => '6',
        7 => '7',
        8 => '8',
        9 => '9',
        10 => 'a',
        11 => 'b',
        12 => 'c',
        13 => 'd',
        14 => 'e',
        15 => 'f',
        _ => panic!("value greater than 15"),
    }
}

pub(crate) fn u64_to_hex(num: u64) -> String {
    let mut hex = String::with_capacity(16); // todo bench capacity

    for i in (0..16).rev() {
        hex.push(u4_to_hexit(((num >> (i * 4)) & 0xf) as u8))
    }

    hex
}

pub(crate) fn rgb_to_hex(r: u8, g: u8, b: u8) -> String {
    let mut color = String::with_capacity(10); // todo bench capacity
    color.push(u4_to_hexit(r >> 4));
    color.push(u4_to_hexit(r & 0xf));
    color.push(u4_to_hexit(g >> 4));
    color.push(u4_to_hexit(g & 0xf));
    color.push(u4_to_hexit(b >> 4));
    color.push(u4_to_hexit(b & 0xf));
    color
}

fn hex_to_u32(s: &str) -> u32 {
    let mut num = 0;
    for c in s.chars() {
        num = num << 4 | hex_to_u32_one(&c);
    }
    num
}

pub(crate) fn hex_color_to_rgb(hex: &str) -> (u32, u32, u32) {
    match hex.len() {
        3 => {
            let r = hex_to_u32(&hex[0..1]);
            let g = hex_to_u32(&hex[1..2]);
            let b = hex_to_u32(&hex[2..3]);

            let r = r << 4 | r;
            let g = g << 4 | g;
            let b = b << 4 | b;
            (r, g, b)
        }
        6 => {
            let r = hex_to_u32(&hex[0..2]);
            let g = hex_to_u32(&hex[2..4]);
            let b = hex_to_u32(&hex[4..6]);
            (r, g, b)
        }
        _ => panic!("unknown hex length"),
    }
}

#[cfg(test)]
mod tests {
    use crate::badge::color::util::{
        brightness, hex_color_to_rgb, hex_to_u32_one, rgb_to_hex, u4_to_hexit,
    };

    #[test]
    fn rgb_to_hex_test() {
        assert_eq!(rgb_to_hex(10, 200, 50), "0ac832")
    }

    #[test]
    fn brightness_test() {
        assert_eq!(brightness(&"#c33".to_string()), 0.3794f32);
        assert_eq!(brightness(&"#afde32".to_string()), 0.73858434f32);
    }

    #[test]
    fn hex_color_tests() {
        assert_eq!(hex_color_to_rgb("0ac832"), (10, 200, 50));
    }

    #[test]
    fn rgb_to_hex_string_test() {
        assert_eq!("0ac832", rgb_to_hex(10, 200, 50));
    }

    #[test]
    fn hex_to_u32_one_test() {
        for (char, num) in ('0'..='9').zip(0..=9) {
            assert_eq!(hex_to_u32_one(&char), num);
        }
        for (char, num) in ('a'..='f').zip(10..=15) {
            assert_eq!(hex_to_u32_one(&char), num);
        }
    }

    #[test]
    fn u4_to_hexit_test() {
        for (char, num) in ('0'..='9').zip(0..=9) {
            assert_eq!(u4_to_hexit(num), char);
        }
        for (char, num) in ('a'..='f').zip(10..=15) {
            assert_eq!(u4_to_hexit(num), char);
        }
    }
}
